package com.itextpdf.text.signature;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeXmlSignature;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
import com.itextpdf.text.pdf.security.SecurityConstants;
import org.apache.jcp.xml.dsig.internal.dom.DOMKeyInfoFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.crypto.*;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.TransformException;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Collections;
/**
* This class is abstract to prevent old Surefire versions from
* running it as a test and failing because it contains no tests.
*/
public abstract class XmlDSigTest {
protected BouncyCastleProvider provider;
protected void initialize() throws Exception {
provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
protected void signXadesWithCertificate(String src, String dest, PrivateKey pk, Certificate[] chain, String digestAlgorithm, String provider, boolean includeSignaturePolicy) throws GeneralSecurityException, IOException, DocumentException, TransformException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
appearance.setDescription("Simple xfa form");
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
//MakeXmlSignature.signXadesBes(appearance, pks, chain);
MakeXmlSignature.signXades(appearance, pks, chain, includeSignaturePolicy);
}
protected void signDsWithCertificate(String src, String dest, PrivateKey pk, Certificate[] chain, String digestAlgorithm, String provider) throws GeneralSecurityException, IOException, DocumentException, TransformException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
//MakeXmlSignature.signXadesBes(appearance, pks, chain);
MakeXmlSignature.signXmlDSig(appearance, pks, chain);
}
protected void signDsWithKeyInfo(String src, String dest, PrivateKey pk, PublicKey publicKey, String digestAlgorithm,
String provider) throws GeneralSecurityException, IOException, DocumentException, TransformException, URIReferenceException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
//Set XfaXmlLocator to control getting and setting Document
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
KeyInfoFactory kif = new DOMKeyInfoFactory();
KeyValue kv = kif.newKeyValue(publicKey);
MakeXmlSignature.signXmlDSig(appearance, pks, kif.newKeyInfo(Collections.singletonList(kv)));
}
protected void signDsWithPublicKey(String src, String dest, PrivateKey pk, PublicKey publicKey, String digestAlgorithm,
String provider) throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
//Set XfaXmlLocator to control getting and setting Document
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
MakeXmlSignature.signXmlDSig(appearance, pks, publicKey);
}
protected void signPackageXadesWithCertificate(String src, String dest, XfaXpathConstructor.XdpPackage xdpPackage, PrivateKey pk, Certificate[] chain, String digestAlgorithm, String provider, boolean includeSignaturePolicy) throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
appearance.setDescription("Simple xfa form");
//Set XfaXmlLocator to control getting and setting Document
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Set XpathConstructor, to construct xpath expression for signing an xdp package
appearance.setXpathConstructor(new XfaXpathConstructor(xdpPackage));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
MakeXmlSignature.signXades(appearance, pks, chain, includeSignaturePolicy);
}
protected void signPackageDsWithCertificate(String src, String dest, XfaXpathConstructor.XdpPackage xdpPackage, PrivateKey pk, Certificate[] chain, String digestAlgorithm, String provider) throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
//Set XfaXmlLocator to control getting and setting Document
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Set XpathConstructor, to construct xpath expression for signing an xdp package
appearance.setXpathConstructor(new XfaXpathConstructor(xdpPackage));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
MakeXmlSignature.signXmlDSig(appearance, pks, chain);
}
protected void signPackageDsWithKeyInfo(String src, String dest, XfaXpathConstructor.XdpPackage xdpPackage, PrivateKey pk, PublicKey publicKey, String digestAlgorithm, String provider) throws GeneralSecurityException, IOException, DocumentException, TransformException, URIReferenceException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
//Set XfaXmlLocator to control getting and setting Document
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Set XpathConstructor, to construct xpath expression for signing an xdp package
appearance.setXpathConstructor(new XfaXpathConstructor(xdpPackage));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
KeyInfoFactory kif = new DOMKeyInfoFactory();
KeyValue kv = kif.newKeyValue(publicKey);
MakeXmlSignature.signXmlDSig(appearance, pks, kif.newKeyInfo(Collections.singletonList(kv)));
}
protected void signPackageDsWithPublicKey(String src, String dest, XfaXpathConstructor.XdpPackage xdpPackage, PrivateKey pk, PublicKey publicKey, String digestAlgorithm, String provider) throws GeneralSecurityException, IOException, DocumentException {
// Creating the reader and the stamper
PdfReader reader = new PdfReader(src);
FileOutputStream os = new FileOutputStream(dest);
PdfStamper stamper = PdfStamper.createXmlSignature(reader, os);
// Creating the appearance
XmlSignatureAppearance appearance = stamper.getXmlSignatureAppearance();
//Set XfaXmlLocator to control getting and setting Document
appearance.setXmlLocator(new XfaXmlLocator(stamper));
// Set XpathConstructor, to construct xpath expression for signing an xdp package
appearance.setXpathConstructor(new XfaXpathConstructor(xdpPackage));
// Creating the signature
ExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
MakeXmlSignature.signXmlDSig(appearance, pks, publicKey);
}
protected String saveXmlFromResult(String input)
throws IOException, ParserConfigurationException, SAXException, TransformerException {
PdfReader reader = new PdfReader(input);
XfaForm form = new XfaForm(reader);
String output = input.replace(".pdf", ".xml");
FileOutputStream file = new FileOutputStream(output);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans = tf.newTransformer();
trans.transform(new DOMSource(form.getDomDocument()), new StreamResult(file));
file.close();
reader.close();
return output;
}
protected boolean compareXmls(String xml1, String xml2) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setCoalescing(true);
dbf.setIgnoringElementContentWhitespace(true);
dbf.setIgnoringComments(true);
DocumentBuilder db = dbf.newDocumentBuilder();
org.w3c.dom.Document doc1 = db.parse(new File(xml1));
doc1.normalizeDocument();
org.w3c.dom.Document doc2 = db.parse(new File(xml2));
doc2.normalizeDocument();
return doc2.isEqualNode(doc1);
}
boolean verifyXmlDSig(String filename) throws Exception {
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(new FileInputStream(filename));
NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nl.getLength() == 0)
throw new Exception("Cannot find Signature element");
NodeList sps = doc.getElementsByTagNameNS(SecurityConstants.XADES_132_URI, "SignedProperties");
if (sps.getLength() > 0)
((Element)sps.item(0)).setIdAttribute("Id", true);
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", new org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI());
DOMValidateContext valContext = new DOMValidateContext
(new KeyValueKeySelector(), nl.item(0));
XMLSignature signature =
fac.unmarshalXMLSignature(valContext);
return signature.validate(valContext);
}
/**
* KeySelector which retrieves the public key out of the
* KeyValue element and returns it.
* NOTE: If the key algorithm doesn't match signature algorithm,
* then the public key will be ignored.
*/
private static class KeyValueKeySelector extends KeySelector {
public KeySelectorResult select(KeyInfo keyInfo,
KeySelector.Purpose purpose,
AlgorithmMethod method,
XMLCryptoContext context)
throws KeySelectorException {
if (keyInfo == null) {
throw new KeySelectorException("Null KeyInfo object!");
}
SignatureMethod sm = (SignatureMethod) method;
if (keyInfo.getContent().isEmpty())
throw new KeySelectorException("No KeyValue element found!");
XMLStructure xmlStructure = (XMLStructure) keyInfo.getContent().get(0);
PublicKey pk = null;
if (xmlStructure instanceof KeyValue) {
try {
pk = ((KeyValue)xmlStructure).getPublicKey();
} catch (KeyException ke) {
throw new KeySelectorException(ke);
}
}
else if (xmlStructure instanceof X509Data) {
X509Data xd = (X509Data) keyInfo.getContent().get(0);
X509Certificate cert = (X509Certificate)xd.getContent().get(0);
pk = cert.getPublicKey();
}
// make sure algorithm is compatible with method
if (pk != null && algEquals(sm.getAlgorithm(), pk.getAlgorithm()))
return new SimpleKeySelectorResult(pk);
throw new KeySelectorException("No KeyValue element found!");
}
static boolean algEquals(String algURI, String algName) {
if (algName.equalsIgnoreCase("DSA") &&
algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
return true;
} else if (algName.equalsIgnoreCase("RSA") &&
algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
return true;
} else {
return false;
}
}
}
private static class SimpleKeySelectorResult implements KeySelectorResult {
private PublicKey pk;
SimpleKeySelectorResult(PublicKey pk) {
this.pk = pk;
}
public Key getKey() { return pk; }
}
}